package icasue.reflect.handles.object;

import icasue.reflect.adaptor.AdaptorManager;
import icasue.reflect.handles.util.OFUtil;
import icasue.reflect.utils.ProxyDigestUtil;
import icasue.reflect.exceptions.HandleException;
import icasue.reflect.handles.HandleSupplier;
import icasue.reflect.handles.OFAble;
import icasue.reflect.handles.classes.ClassOF;
import icasue.reflect.handles.exception.ExceptionOF;
import icasue.reflect.handles.method.MethodOF;
import icasue.reflect.handles.predicate.SureOF;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @Author: Qiao Hang
 * @CreateDate: 2020/12/1 下午1:57
 * @UpdateDate:
 * @Description:
 */
public class ObjectOF extends AdaptorManager implements OFAble {

    /**
     *  isNull_  (obj)
     *  notNull_ (obj)
     *  toString_ (obj)  doc: if obj which you supplied is null, return "null"
     *  getNull_ ()      doc: return null consist.
     *  equals_ (left,right)
     *  isPrivate_ (int)
     *  isStatic_ (int)
     *  isPublic_ (int)
     *  notEmptyAry_ (ary)     doc: return true if elements all not null in ary, and elements's count bigger zero.
     *  notEmptyList_ (list)     doc: return true if elements all not null in list, and elements's count bigger zero.
     *  isPrivate (integer)     doc: return if equals this int val which you supplied and Modify.PRIVATE.
     *  findSuitInstance_ (type)
     *      doc: try find and return an instance from environment, if not found, then try call newInstance to construct this instance
     *  newInstance_   (type)
     *      doc: try invoke construct to get a instance.
     *
     *  All method check pass.
     */
    public static Predicate<Object> isNull_ = (obj) -> {
        try {
            return (boolean)ObjectO.isNull.invoke(obj);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.isNull_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> notNull_ = (obj) -> {
        try {
            return (boolean)ObjectO.notNull.invoke(obj);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.notNull_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.PredicateAry notNullAll_ = (params) -> {
        try {
            return ObjectOF.notEmptyAry_.test(params);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.notNullAll_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Object> toString_ = (obj) -> {
        try {
            return ObjectO.toString.invoke(obj);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.toString_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Supplier<Object> getNull_ = () -> {
        try {
            return ObjectO.getNull.invoke();
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.getNull_ : occur an error : " , throwable.getMessage());
        }
    };

    public static HandleSupplier.PredicateAry equals_ = (leftRight) -> {
        try {
            if(leftRight.length != 2)
                throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.equals_ : required 2 prams.");
            return (boolean)ObjectO.equals.invoke(leftRight[0],leftRight[1]);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.equals_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> notEmptyAry_ = (ary) -> {
        try {
            return ObjectOF.notNull_.test(ary) && ClassOF.isArray_.test(ary.getClass()) && Tools.Check.fullObjects((Object[]) ary);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.notEmptyAry_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> notEmptyList_ = (list) -> {
        try {
            return ObjectOF.notNull_.test(list) && ClassOF.isInstance_.test(new Object[]{List.class,list}) && Tools.Check.fullList((List) list);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.notEmptyList_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isPrivate_ = (obj) -> {
        try {
            SureOF.notNull_.accept(obj);
            SureOF.isInt_.accept(obj);
            return Modifier.isPrivate(Integer.class.cast(obj));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.isPrivate : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isPublic_ = (obj) -> {
        try {
            SureOF.notNull_.accept(obj);
            SureOF.isInt_.accept(obj);
            return Modifier.isPublic(Integer.class.cast(obj));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.isPublic : occur an error : " , throwable.getMessage());
        }
    };

    public static Predicate<Object> isStatic_ = (obj) -> {
        try {
            SureOF.notNull_.accept(obj);
            SureOF.isInt_.accept(obj);
            return Modifier.isStatic(Integer.class.cast(obj));
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.isStatic : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Object> findSuitInstance_ = (cls) -> {
        try {
            SureOF.isCls_.accept(cls);
            Object suitableInstance = ObjectOF.getNull_.get();

            // try check base module load or not, stronger this function.
            ApplicationTouch baseAppTouch = getInst(ApplicationTouch.class);
            if(baseAppTouch.modelIfLoad()){

                // ApplicationTouch in base module is ready, means method ready() return Boolean.TRUE.
                if(baseAppTouch.exec("ready", null, null, Boolean.class).get()){
                    //find instance from proxyBufferTree.
                    Object[] hasBeenProxied = (Object[]) baseAppTouch.fieldGet("strongerProxyOriginRef", null, Map.class)
                            .get().get(cls);
                    if(hasBeenProxied != null && hasBeenProxied.length > 0) {
                        suitableInstance = hasBeenProxied[0];
                        return suitableInstance;
                    }

                    //find instance from environment.
                    Object applicationContext = baseAppTouch.fieldGet("applicationContext", null, Object.class).get();
                    Object beanFactory = baseAppTouch.fieldGet("beanFactory", null, Object.class).get();

                    try {
                        Map beans = (Map) OFUtil.cast(MethodOF.findMethod_mType_mName_paramClassArray.apply(
                                applicationContext.getClass(),
                                "getBeansOfType",
                                new Class[]{Class.class}
                        ),Method.class).invoke(applicationContext,cls);
                        if(beans == null || beans.size() == 0){
                            suitableInstance = ObjectOF.getNull_.get();
                        }else {
                            suitableInstance = beans.values().stream().findFirst().get();
                        }
                    }catch (Throwable e){
                        try {
                            suitableInstance = OFUtil.cast(MethodOF.findMethod_mType_mName_paramClassArray.apply(
                                    applicationContext.getClass(),
                                    "getBean",
                                    new Class[]{Class.class}
                            ),Method.class).invoke(beanFactory,cls);
                        }catch (Throwable e2){
                            suitableInstance = ObjectOF.getNull_.get();
                        }
                    }
                }
            }
            if(ObjectOF.isNull_.test(suitableInstance)){
                suitableInstance = ObjectOF.newInstance_.apply(cls);
                if(ObjectOF.notNull_.test(suitableInstance))
                    return suitableInstance;
                throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.findSuitInstance_ : can't find suitable instance from environment" +
                        ", and newInstance failed, check if exist noArgs construct pls.");
            }
            return ProxyDigestUtil.exposeOrigInstanceUnderCglib(suitableInstance).orElse(null);
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.findSuitInstance_ : occur an error : " , throwable.getMessage());
        }
    };

    public static Function<Object,Object> newInstance_ = (cls) -> {
        try {
            SureOF.isCls_.accept(cls);
            Object constructInstance;
            try { constructInstance = ClassOF.newInstance_.apply(cls); } catch (Throwable e3){
                throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.newInstance_ : newInstance failed, check if exist noArgs construct pls.");
            }
            return constructInstance;
        } catch (HandleException deepError){
            throw deepError;
        } catch (Throwable throwable) {
            throw ExceptionOF.handleExcInvocation_.apply("ObjectOF.newInstance_ : occur an error : " , throwable.getMessage());
        }
    };
    

}
